import { BehaviorSubject, Observable, Observer } from 'rxjs'
import { IOption, IFormItem, FormItemType } from '@shared/interfaces/common'
import { numScale, compareTwoCurrencies } from 'app/utils/helpers'
import { ALPChannel } from '@shared/interfaces/acquirer'
import { Validators, FormControl, ValidationErrors, ValidatorFn, FormGroup } from '@angular/forms'
import {
  AlipayPartnerIDMaxLength,
  AlipayPartnerIDReg,
  TransactionFeeReg,
  MD5KeyMaxLength,
  MD5KeyReg,
  ChannelMerchantNameMaxLength,
  SFTPUsernameMaxLength,
  SFTPUsernameReg,
  SFTPPasswordMaxLength,
  SFTPPasswordReg,
  SFTPPathMaxLength,
  SFTPPathReg,
  ALPAppIdMaxLength,
  ALPAppIdReg,
} from 'app/utils/contraints'
import { I18NService } from '@core'
import { Injectable } from '@angular/core'

@Injectable()
export class ALPService {

  isSupportAuthHidden$ = new BehaviorSubject<boolean>(true)
  isSettlementCurrHidden$ = new BehaviorSubject<boolean>(true)
  isSignKeyHidden$ = new BehaviorSubject<boolean>(true)
  isCurrencySpan$ = new BehaviorSubject<string>('24')
  isPaymentSpan$ = new BehaviorSubject<string>('24')
  isPreAuthSpan$ = new BehaviorSubject<string>('24')

  constructor(private i18n: I18NService) {}

  genALPFormItems = (
    currencyOptions: IOption[],
    currencyCodeOptions: IOption[],
    handleChanMerIDChange,
  ): IFormItem[] => {
    this.isSupportAuthHidden$.next(true)
    this.isSettlementCurrHidden$.next(true)
    this.isSignKeyHidden$.next(true)
    return [
      {
        label: 'Alipay Partner ID',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'Alipay Partner ID' }),
          existed: this.trans('VALIDATION.CODE_EXISTS', { name: 'Alipay Partner ID' }),
          maxLength: this.trans('VALIDATION.MAX_LENGTH', { length: AlipayPartnerIDMaxLength }),
          pattern: this.trans('VALIDATION.PATTERN.NUMBER_ONLY'),
        },
        formControlName: 'chanMerID',
        change: handleChanMerIDChange,
      },
      {
        label: 'Alipay Partner ID Type: Payment',
        noColon: true,
        errTips: {
          required: this.trans('VALIDATION.SWITCH.ALP_PAYMENT_OR_PRE-AUTH'),
        },
        asyncSpan: this.isPaymentSpan$,
        errTip: '',
        formControlName: 'isSupportPayment',
        type: FormItemType.SWITCH,
        change: (isSupport: boolean) => {
          this.isSignKeyHidden$.next(!isSupport)
          this.isPaymentSpan$.next(isSupport ? '12' : '24')
        },
      },
      {
        label: 'MD5 Key',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'MD5 Key' }),
          maxLength: this.trans('VALIDATION.MAX_LENGTH', { length: MD5KeyMaxLength }),
          pattern: this.trans('VALIDATION.PATTERN.ALPHANUMERIC_ONLY'),
        },
        formControlName: 'signKey',
        asyncHidden: this.isSignKeyHidden$,
      },
      {
        label: 'Alipay Partner ID Type: Pre-authorization',
        noColon: true,
        errTips: {
          required: this.trans('VALIDATION.SWITCH.ALP_PAYMENT_OR_PRE-AUTH'),
        },
        formControlName: 'isSupportPreAuth',
        asyncSpan: this.isPreAuthSpan$,
        type: FormItemType.SWITCH,
        change: (isSupport: boolean) => {
          this.isSupportAuthHidden$.next(!isSupport)
          this.isPreAuthSpan$.next(isSupport ? '12' : '24')
        },
      },
      {
        label: 'alpAppID',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'alpAppID' }),
          maxLength: this.trans('VALIDATION.MAX_LENGTH', { length: ALPAppIdMaxLength }),
          pattern: this.trans('VALIDATION.PATTERN.ALPHANUMERIC_ONLY'),
        },
        formControlName: 'appID',
        asyncHidden: this.isSupportAuthHidden$,
      },
      {
        label: 'Alipay RSA Public Key',
        errTip: '',
        type: FormItemType.FILE,
        formControlName: 'publicKey',
        asyncHidden: this.isSupportAuthHidden$,
      },
      {
        label: 'RSA Private Key',
        errTip: '',
        type: FormItemType.FILE,
        formControlName: 'privateKeyArray',
        asyncHidden: this.isSupportAuthHidden$,
      },
      {
        label: 'Alipay Partner ID Type: Alipay Currency Conversion',
        noColon: true,
        errTips: {
          required: this.trans('VALIDATION.SWITCH.ALP_PAYMENT_OR_PRE-AUTH'),
        },
        asyncSpan: this.isCurrencySpan$,
        errTip: '',
        formControlName: 'isSupportSettleCurr',
        type: FormItemType.SWITCH,
        change: (isSupport: boolean) => {
          this.isSettlementCurrHidden$.next(!isSupport)
          this.isCurrencySpan$.next(isSupport ? '12' : '24')
        },
      },
      {
        label: 'Settlement Currency',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'Settlement Currency' }),
        },
        formControlName: 'settleCurrency',
        type: FormItemType.CURRENCY_SELECT,
        currencyType: 'string',
        multiple: 'default',
        allowClear: true,
        compareFn: compareTwoCurrencies,
        asyncHidden: this.isSettlementCurrHidden$,
      },
      {
        label: 'Channel Merchant Name',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'Channel Merchant Name' }),
          maxLength: this.trans('VALIDATION.MAX_LENGTH', { length: ChannelMerchantNameMaxLength }),
        },
        formControlName: 'chanMerName',
      },
      {
        label: 'Transaction Currency',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'Transaction Currency' }),
        },
        formControlName: 'currencies',
        type: FormItemType.CURRENCY_SELECT,
        multiple: 'multiple',
        allowClear: true,
        compareFn: compareTwoCurrencies,
        options: currencyOptions,
      },
      {
        label: 'Transaction Fee',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'Transaction Fee' }),
          pattern: this.trans('VALIDATION.PATTERN.FOUR_DECIMAL_MAX_AND_NO_NEGATIVE'),
        },
        type: FormItemType.INPUTGROUP,
        addAfter: '%',
        errTip: `Transaction Fee ${this.trans('VALIDATION.PATTERN.BETWEEN_ZERO_AND_TEN_PERCENT')}`,
        formControlName: 'fee',
        inputType: 'number',
      },
      {
        label: 'Settlement Type',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'Settlement Type' }),
        },
        formControlName: 'settType',
        type: FormItemType.RADIO,
        options: [
          {
            label: 'settlement',
            value: 0,
          },
          {
            label: 'transaction',
            value: 1,
          }
        ]
      },
      {
        label: 'SFTP username',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'SFTP username' }),
          maxLength: this.trans('VALIDATION.MAX_LENGTH', { length: SFTPUsernameMaxLength }),
          pattern: this.trans('VALIDATION.PATTERN.ALPHANUMERIC_ONLY'),
        },
        formControlName: 'sftpUsername',
      },
      {
        label: 'SFTP password',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'SFTP password' }),
          maxLength: this.trans('VALIDATION.MAX_LENGTH', { length: SFTPPasswordMaxLength }),
          pattern: this.trans('VALIDATION.PATTERN.ALPHANUMERIC_ONLY'),
        },
        inputType: 'password',
        formControlName: 'sftpPassword',
      },
      {
        label: 'SFTP Path',
        errTips: {
          required: this.trans('VALIDATION.REQUIRED', { name: 'SFTP Path' }),
          maxLength: this.trans('VALIDATION.MAX_LENGTH', { length: SFTPPathMaxLength }),
          pattern: this.trans('VALIDATION.PATTERN.ALPHANUMERIC_SYMBOLS_ONLY'),
        },
        formControlName: 'sftpFilePath',
      },
    ]
  }

  genALPValidateRules = (channel: ALPChannel): { [key: string]: any } => {
    const preAuthAsyncValidator = this.getValidator(this.isSupportAuthHidden$, [
      Validators.required,
      Validators.maxLength(ALPAppIdMaxLength),
      Validators.pattern(ALPAppIdReg),
    ])
    const publicKeyAsyncValidator = this.getValidator(this.isSupportAuthHidden$, [Validators.required])
    const privateKeyArrayAsyncValidator = this.getValidator(this.isSupportAuthHidden$, [Validators.required])
    const settleCurrencyValidator = this.getValidator(this.isSettlementCurrHidden$, [Validators.required])
    const signKeyValidator = this.getValidator(this.isSignKeyHidden$, [
      Validators.required,
      Validators.maxLength(MD5KeyMaxLength),
      Validators.pattern(MD5KeyReg),
    ])

    return {
      id: [null, []],
      chanCode: [channel && channel.chanCode, [Validators.required]],
      chanMerID: [
        channel && channel.chanMerID,
        [Validators.required, Validators.maxLength(AlipayPartnerIDMaxLength), Validators.pattern(AlipayPartnerIDReg)],
      ],
      currencies: [channel && channel.currencies, [Validators.required]],
      fee: [
        channel && numScale(channel.fee, 2),
        [Validators.required, Validators.min(0), Validators.max(10), Validators.pattern(TransactionFeeReg)],
      ],
      isSupportPayment: [(channel && channel.signKey) ? true : false, [], []],
      signKey: [channel && channel.signKey, [], [signKeyValidator]],
      chanMerName: [
        channel && channel.chanMerName,
        [Validators.required, Validators.maxLength(ChannelMerchantNameMaxLength)],
      ],
      isSupportSettleCurr: [(channel && channel.settleCurrency) ? true : false, []],
      settleCurrency: [channel && (channel as ALPChannel).settleCurrency, [], [settleCurrencyValidator]],
      settType: [ channel && (channel as ALPChannel).settType || 0, [Validators.required] ],
      sftpUsername: [
        channel && (channel as ALPChannel).sftp.username,
        [Validators.required, Validators.maxLength(SFTPUsernameMaxLength), Validators.pattern(SFTPUsernameReg)],
      ],
      sftpPassword: [
        channel && (channel as ALPChannel).sftp.password,
        [Validators.required, Validators.maxLength(SFTPPasswordMaxLength), Validators.pattern(SFTPPasswordReg)],
      ],
      sftpFilePath: [
        channel && (channel as ALPChannel).sftp.filePath,
        [Validators.required, Validators.maxLength(SFTPPathMaxLength), Validators.pattern(SFTPPathReg)],
      ],
      isSupportPreAuth: [channel && (channel as ALPChannel).isSupportPreAuth, []],
      appID: [channel && channel.appID, [], [preAuthAsyncValidator]],
      publicKey: [channel && channel.publicKey, [], [publicKeyAsyncValidator]],
      privateKeyArray: [channel && channel.privateKeyArray, [], [privateKeyArrayAsyncValidator]],
      isLoaded: [ channel && channel.isLoaded],
    }
  }

  genALPAdditionalValidateRules = (channel: ALPChannel) => {
    return {
      validator: this.modeAtLeastOne(),
    }
  }

  modeAtLeastOne = () => {
    return (formGroup: FormGroup) => {
      const isPaymentCtrl = formGroup.controls['isSupportPayment']
      const isPreAuthCtrl = formGroup.controls['isSupportPreAuth']
      if (!isPaymentCtrl.value && !isPreAuthCtrl.value) {
        isPaymentCtrl.setErrors({ required: true })
        isPreAuthCtrl.setErrors({ required: true })
      } else {
        isPaymentCtrl.setErrors(null)
        isPreAuthCtrl.setErrors(null)
      }
    }
  }

  getValidator = (hidden$, validators: ValidatorFn[]) => (control: FormControl) => {
    return new Observable((observer: Observer<ValidationErrors | null>) => {
      control.setValidators(validators)
      hidden$.subscribe(isHidden => {
        if (isHidden) {
          control.clearValidators()
          observer.next(null)
          observer.complete()
          return
        }
        const valid = control.valid
        if (!valid) {
          observer.next(control.errors)
        }
        observer.complete()
      })
    })
  }

  private trans(str: string, params?: Object): string {
    return this.i18n.fanyi(str, params)
  }
}
